﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vds2465Telegrams;

namespace Vds2465Parser
{
	internal static class ControlField
	{
        #region private constants

        private const byte functionCode_REQ_NDAT = 10;
        private const byte functionCode_REQ_STATUS = 9;
        private const byte functionCode_SEND_NDAT = 3;
        private const byte functionCode_SEND_NORM = 0;
        
		private const byte functionCodes_CONFIRM_ACK = 0;
        private const byte functionCodes_CONFIRM_NAK = 1;
        private const byte functionCodes_FC_ERROR = 6;
        private const byte functionCodes_RESPOND_NAK = 9;
        private const byte functionCodes_RESPOND_NDAT = 8;
        private const byte functionCodes_RESPOND_STATUS = 11;

        #endregion private constants

		#region internal methods

        internal static byte Generate_ControlField_Calling(eDirection direction, bool frameCountValid, bool frameCountFlag, eTelegramFunction_Calling telegramFunction)
		{
			byte controlField = 0;

            // MSB - Bit 7
            byte directionValue = GenerateDirection(direction);
            controlField += directionValue;

			controlField += 64;     // Bit 6 is always set for a calling telegram ("Aufrufrichtung")

			if (frameCountFlag)
			{
				controlField += 32; // Bit 5: FCB
			}

			if (frameCountValid)
			{				
				controlField += 16; // Bit 4: FCV
			}

            byte functionCodes = GenerateFunctionCodes_Calling(telegramFunction);
			controlField += functionCodes;

			return controlField;
		}

		internal static byte Generate_ControlField_Returning(eDirection direction, bool accessDemandFlag, bool dataFlowControlStopFlag, eTelegramFunction_Returning telegramFunction)
		{
			byte controlField = 0;

            // MSB - Bit 7
            byte directionValue = GenerateDirection(direction);
            controlField += directionValue;

			// Bit 6 is always NOT set for a returning telegram ("Antwortrichtung")

			if (accessDemandFlag)
			{
				controlField += 32; // Bit 5: ACD				
			}

			if (dataFlowControlStopFlag)
			{
				controlField += 16; // Bit 4: DFC
			}

            byte functionCodes = GenerateFunctionCodes_Returning(telegramFunction);
			controlField += functionCodes;

			return controlField;
		}

        internal static void IsThisCallingOrReturning(byte controlField, out bool calling)
        {
            // if the 6 th bit (64) set then it is calling, otherwise it is returning
            byte bit6Only = (byte)((byte) controlField & (byte) 0x40);
            calling = bit6Only > 0;
        }

        internal static bool Parse_ControlField_Calling(byte controlField, 
            out eDirection direction, out bool frameCountValid, out bool frameCountFlag, out eTelegramFunction_Calling telegramFunctionCalling)
        {
            // default value
            telegramFunctionCalling = eTelegramFunction_Calling.REQ_NDAT;

            // parse values
            direction = ParseDirection(controlField);
            frameCountValid = ParseFrameCountValid(controlField);
            frameCountFlag = ParseFrameCountFlag(controlField);
            bool bRetVal = ParseFunctionCodes_Calling(controlField, out telegramFunctionCalling);

            return bRetVal;
        }

        internal static bool Parse_ControlField_Returning(byte controlField, 
            out eDirection direction, out bool accessDemandFlag, out bool dataFlowControlStopFlag, out eTelegramFunction_Returning telegramFunctionReturning)
        {
            // default value
            telegramFunctionReturning = eTelegramFunction_Returning.FC_ERROR;

            // parse values
            direction = ParseDirection(controlField);
            accessDemandFlag = ParseAccessDemandFlag(controlField);
            dataFlowControlStopFlag = ParseDataFlowControlStop(controlField);
            bool bRetVal = ParseFunctionCodes_Returning(controlField, out telegramFunctionReturning);

            return bRetVal;
        }

		#endregion internal methods

		#region private methods

        private static byte GenerateDirection(eDirection direction)
        {
            if (direction == eDirection.ATE_to_RCT)
            {
                return 128;
            }

            return 0;
        }

        private static eDirection ParseDirection(byte controlField)
        {
            byte tmpControlField = (byte) ((byte) controlField & (byte) 0x80);
            if (tmpControlField > 0)
            {
                return eDirection.ATE_to_RCT;
            }

            return eDirection.RCT_to_ACE;
        }

        private static bool ParseFrameCountValid(byte controlField)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x10);
            return tmpControlField > 0;            
        }

        private static bool ParseFrameCountFlag(byte controlField)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x20);
            return tmpControlField > 0;
        }

        private static bool ParseDataFlowControlStop(byte controlField)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x10);
            return tmpControlField > 0;
        }

        private static bool ParseAccessDemandFlag(byte controlField)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x20);
            return tmpControlField > 0;
        }
                
		/// <summary>
		/// Generate the function codes value, i.e. the low half byte of the control field
		/// </summary>
		/// <param name="telegramType"></param>
		/// <returns></returns>
        private static byte GenerateFunctionCodes_Calling(eTelegramFunction_Calling telegramFunction)
		{
			byte functionCodes = 0;

            switch (telegramFunction)
			{
                case eTelegramFunction_Calling.REQ_NDAT:
					functionCodes = functionCode_REQ_NDAT;
					break;
                case eTelegramFunction_Calling.REQ_STATUS:
					functionCodes = functionCode_REQ_STATUS;
					break;
                case eTelegramFunction_Calling.SEND_NDAT:
					functionCodes = functionCode_SEND_NDAT;
					break;
                case eTelegramFunction_Calling.SEND_NORM:
					functionCodes = functionCode_SEND_NORM;
					break;
				default:
					// unknown !!!!
					// TODO handle error
					break;
			}

			return functionCodes;
		}

        private static bool ParseFunctionCodes_Calling(byte controlField, out eTelegramFunction_Calling telegramFunctionCalling)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x0f);
            bool bRetVal = false;
            // set dummy value
            telegramFunctionCalling = eTelegramFunction_Calling.REQ_NDAT;
            switch (tmpControlField)
            {
                case functionCode_REQ_NDAT:
                    telegramFunctionCalling = eTelegramFunction_Calling.REQ_NDAT;
                    bRetVal = true;
                    break;
                case functionCode_REQ_STATUS:
                    telegramFunctionCalling = eTelegramFunction_Calling.REQ_STATUS;
                    bRetVal = true;
                    break;
                case functionCode_SEND_NDAT:
                    telegramFunctionCalling = eTelegramFunction_Calling.SEND_NDAT;
                    bRetVal = true;
                    break;
                case functionCode_SEND_NORM:
                    telegramFunctionCalling = eTelegramFunction_Calling.SEND_NORM;
                    bRetVal = true;
                    break;
                default:
                    // unknown !!!!
                    // TODO handle error
                    break;
            }

            return bRetVal;
        }

		/// <summary>
		/// Generate the function codes value, i.e. the low half byte of the control field
		/// </summary>
		/// <param name="telegramType"></param>
		/// <returns></returns>
        private static byte GenerateFunctionCodes_Returning(eTelegramFunction_Returning telegramFunction)
		{
			byte functionCodes = 0;

            switch (telegramFunction)
			{
                case eTelegramFunction_Returning.CONFIRM_ACK:
					functionCodes = functionCodes_CONFIRM_ACK;
					break;
                case eTelegramFunction_Returning.CONFIRM_NAK:
					functionCodes = functionCodes_CONFIRM_NAK;
					break;
                case eTelegramFunction_Returning.FC_ERROR:
					functionCodes = functionCodes_FC_ERROR;
					break;
                case eTelegramFunction_Returning.RESPOND_NAK:
					functionCodes = functionCodes_RESPOND_NAK;
					break;
                case eTelegramFunction_Returning.RESPOND_NDAT:
					functionCodes = functionCodes_RESPOND_NDAT;
					break;
                case eTelegramFunction_Returning.RESPOND_STATUS:
					functionCodes = functionCodes_RESPOND_STATUS;
					break;
				default:
					// unknown !!!!
					// TODO handle error
					break;
			}

			return functionCodes;
		}

        private static bool ParseFunctionCodes_Returning(byte controlField, out eTelegramFunction_Returning telegramFunctionReturning)
        {
            byte tmpControlField = (byte)((byte)controlField & (byte)0x0f);
            bool bRetVal = false;
            // set dummy value
            telegramFunctionReturning = eTelegramFunction_Returning.FC_ERROR;
            switch (tmpControlField)
            {
                case functionCodes_CONFIRM_ACK:
                    telegramFunctionReturning = eTelegramFunction_Returning.CONFIRM_ACK;
                    bRetVal = true;
                    break;
                case functionCodes_CONFIRM_NAK:
                    telegramFunctionReturning = eTelegramFunction_Returning.CONFIRM_NAK;
                    bRetVal = true;
                    break;
                case functionCodes_FC_ERROR:
                    telegramFunctionReturning = eTelegramFunction_Returning.FC_ERROR;
                    bRetVal = true;
                    break;
                case functionCodes_RESPOND_NAK:
                    telegramFunctionReturning = eTelegramFunction_Returning.RESPOND_NAK;
                    bRetVal = true;
                    break;
                case functionCodes_RESPOND_NDAT:
                    telegramFunctionReturning = eTelegramFunction_Returning.RESPOND_NDAT;
                    bRetVal = true;
                    break;
                case functionCodes_RESPOND_STATUS:
                    telegramFunctionReturning = eTelegramFunction_Returning.RESPOND_STATUS;
                    bRetVal = true;
                    break;
                default:
                    // unknown !!!!
					// TODO handle error
                    break;
            }

            return bRetVal;
        }

		#endregion private methods
	}
}
